/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package userservercontroller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import javax.swing.Timer;
import repository.LongComparable;

/**
 * Кдасс, необходимый для учёта активности клиента. Клиент с интервалом t
 * пингует сервер, сервер на пинг клиента устанавливает его контрольный счетчик, если
 * за время Т, от клиента не приходит не одного пинга, то считается что связь с клиентом
 * потеряна.
 * @author Volk ID = 1001
 */
class PingNetworkTable {
  
  //--------------------------Атрибуты класса------------------------------------------------------------
  private volatile int controlTime = 20000; //Контрольное время в миллисекундах
  private volatile int controlIntervalCount = 3; //Контрольных интервалов в периоде полной идентификации

  private ConcurrentHashMap<LongComparable,LongComparable> table; //Таблица в которой хранятся сведения об авторизации
  private ReadLock readLock;  //Блокировка по чтению
  private WriteLock writeLock; //Блокировка по записи
  private NetworkTable netTable; //Интерфейс для хранения сетевой таблицы

  private Timer timer; //Таймер отсчёта
  //------------------------------------------------------------------------------------------------------

  //--------------------------------Сервисные методы------------------------------------------------------

  /**
   * Запустить сервис по контролю за клиентским соединением
   */
  private void timerInit(){
      ActionListener listener = new ActionListener(){

       /**
        * Проход по таблице с целью уменьшить счётчик пингов и удаление клиентов которые не прошли
        * данный анализ и были признаны не подключёнными к сети
        */
       public void actionPerformed(ActionEvent e) {
         readLock.lock();

         Iterator<Entry<LongComparable, LongComparable>> iterator = table.entrySet().iterator();
         Entry<LongComparable, LongComparable> temp;
         while(iterator.hasNext()){
            temp = iterator.next();
            temp.getValue().value--;
            if(temp.getValue().value == 0){
              //Производим удаление так как пользователь призан отключившимся от сети
              LongComparable key = temp.getKey();
              System.out.println("Признаём пользователя " + key.value + " отключенным от сети");
              readLock.unlock();
              netTable.deleteKey(key.value);
              readLock.lock();
            }
         }

        readLock.unlock();
       }
      };

      if(timer != null) timer.stop();
      timer = new Timer(controlTime, listener);
      timer.start();
  }
  //------------------------------------------------------------------------------------------------------

  //--------------------------------Интерфейсные методы----------------------------------------------------

  public PingNetworkTable(NetworkTable netTable){
    table = new ConcurrentHashMap<LongComparable,LongComparable>();
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    readLock = lock.readLock();
    writeLock = lock.writeLock();
    this.netTable = netTable;
    timerInit();
  }

  /**
   * Добавит авторизационный ключ в таблицу
   * @param magicKey - авторизационный ключ
   * @return true - в случае если авторизационный ключ успешно добавлен, false - данный авторизационный ключ уже существует в таблице
   */
  public boolean add(long magicKey){
    writeLock.lock();
    LongComparable key = new LongComparable(magicKey);
    if(table.containsKey(key)){
      writeLock.unlock();
      return false;
    }
    table.put(key, new LongComparable(controlIntervalCount));
    writeLock.unlock();
    return true;
  }

  /**
   * Удалить авторизационный ключ из таблицы
   * @param magicKey - авторизационный ключ
   * @return true - запись успешно удалена, false - записи с таким авторизационным ключом нет в таблице
   */
  public boolean delete(long magicKey){
    writeLock.lock();
    LongComparable key = new LongComparable(magicKey);
    if(!table.containsKey(key)){
     writeLock.unlock();
     return false;
    }
    table.remove(key);
    writeLock.unlock();
    return true;
  }

  /**
   * Отклик пользователя
   * @param autorizationKey авторизационный ключ пользователя
   * @return true - счетчик успешно обновлён, false - ошибка при обновлениий счётчика, пользователь не авторизован
   */
  public boolean ping(long autorizationKey){
     LongComparable key =  new LongComparable(autorizationKey);
     if(!table.containsKey(key)) return false;
     table.get(key).value = controlIntervalCount;
     return true;
  }

  /**
   * Получить контрольное время проверки
   * @return
   */
  public long getControlTime(){
   return controlTime;
  }

  /**
   * Установить новое контрольное время
   * @param controlTime
   */
  public void setControlTime(int controlTime){
   this.controlTime = controlTime;
  }

  /**
   * Получить количество контрольных интервалов по истечению которых клиент будет признан отключенным
   * @return количество контрольных интервалов
   */
  public long getControlIntervalCount(){
   return controlIntervalCount;
  }

  /**
   * Установить количество контрольных интервалов по истечению которых клиент будет признан отключенным
   * @param controlIntervalTime - новое количество контрольных интервалов
   */
  public void setControlIntervalCount(int controlIntervalCount){
   this.controlIntervalCount = controlIntervalCount;
   timerInit();
  }

  /**
   * Очистить содержимое таблицы пингов
   */
  public void clear(){
   table.clear();
  }

  //--------------------------------------------------------------------------------------------------
}
